﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using MahApps.Metro.Controls;
using System.IO;
using System.Xml;
using ICSharpCode.AvalonEdit.Highlighting;
using System.Text.RegularExpressions;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// CssEditor.xaml 的交互逻辑
    /// </summary>
    public partial class CssEditor : MetroWindow
    {
        /// <summary>
        /// 自定义高亮定义，完整定义。
        /// 但一千多行的帮助文档就明显延迟。
        /// </summary>
        private static IHighlightingDefinition cssHighlighting;

        public static string TitleText { get { return Globals.AppName + Globals.MainWindow.GetMetaFileTitleOfDirectory(Globals.PathOfWorkspace) + "- CSS 编辑器"; } }

        private static Converter.ModifiedToText modifiedToTextConverter = new LunarMarkdownEditor.Converter.ModifiedToText();
        /// <summary>
        /// [只读]bool to "已修改 未修改" 的字符串的转换器。
        /// </summary>
        public static Converter.ModifiedToText ModifiedToTextConverter { get { return modifiedToTextConverter; } }

        /// <summary>
        /// [静态构造方法]
        /// </summary>
        static CssEditor()
        {
            Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("LunarSF.SHomeWorkshop.LunarMarkdownEditor.CSS.xshd");
            if (s != null)
            {
                using (XmlReader reader = new XmlTextReader(s))
                {
                    cssHighlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.
                        HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }
        }

        /// <summary>
        /// [构造方法]
        /// </summary>
        public CssEditor(string cssFileName)
        {
            InitializeComponent();

            this.Title = Globals.AppName + " - 自定义 CSS 编辑器";

            switch (cssFileName.ToLower())
            {
                case "lesson_light":
                    {
                        this.lmeCssFullPath = Globals.PathOfWorkspace + cssFileName + ".css";
                        tbLmeCssPrompt.Text = "LME 内置【明】系主题：";
                        tbCustomCssPrompt.Text = "自定义【明】系 CSS 片段：";
                        this.customCssFullPath = Globals.PathOfWorkspace + "custom_" + cssFileName + ".css";

                        mainGrid.Background = Brushes.LightGray;
                        gs.Background = Brushes.WhiteSmoke;
                        tbLmeCssPrompt.Foreground =
                            tbCustomCssPrompt.Foreground = Brushes.Black;
                        break;
                    }
                case "lesson_dark":
                    {
                        this.lmeCssFullPath = Globals.PathOfWorkspace + cssFileName + ".css";
                        tbLmeCssPrompt.Text = "LME 内置【暗】系主题：";
                        tbCustomCssPrompt.Text = "自定义【暗】系 CSS 片段：";
                        this.customCssFullPath = Globals.PathOfWorkspace + "custom_" + cssFileName + ".css";

                        mainGrid.Background = Brushes.DimGray;
                        gs.Background = Brushes.DarkGray;
                        tbLmeCssPrompt.Foreground =
                            tbCustomCssPrompt.Foreground = Brushes.LightGray;
                        break;
                    }
                case "menu_light":
                    {
                        this.lmeCssFullPath = Globals.PathOfWorkspace + cssFileName + ".css";
                        tbLmeCssPrompt.Text = "LME 内置【明】系左边栏样式：";
                        tbCustomCssPrompt.Text = "自定义【明】系左边栏 CSS 片段：";
                        this.customCssFullPath = Globals.PathOfWorkspace + "custom_" + cssFileName + ".css";

                        mainGrid.Background = Brushes.LightGray;
                        gs.Background = Brushes.WhiteSmoke;
                        tbLmeCssPrompt.Foreground =
                            tbCustomCssPrompt.Foreground = Brushes.Black;
                        break;
                    }
                case "menu_dark":
                    {
                        this.lmeCssFullPath = Globals.PathOfWorkspace + cssFileName + ".css";
                        tbLmeCssPrompt.Text = "LME 内置【暗】系左边栏样式：";
                        tbCustomCssPrompt.Text = "自定义【暗】系左栏栏 CSS 片段：";
                        this.customCssFullPath = Globals.PathOfWorkspace + "custom_" + cssFileName + ".css";

                        mainGrid.Background = Brushes.DimGray;
                        gs.Background = Brushes.DarkGray;
                        tbLmeCssPrompt.Foreground =
                            tbCustomCssPrompt.Foreground = Brushes.LightGray;
                        break;
                    }
                case "presentation_light":
                    {
                        this.lmeCssFullPath = Globals.PathOfWorkspace + cssFileName + ".css";
                        tbLmeCssPrompt.Text = "LME 内置【明】系演示主题样式：";
                        tbCustomCssPrompt.Text = "自定义【明】系演示主题 CSS 片段：";
                        this.customCssFullPath = Globals.PathOfWorkspace + "custom_" + cssFileName + ".css";

                        mainGrid.Background = Brushes.LightGray;
                        gs.Background = Brushes.WhiteSmoke;
                        tbLmeCssPrompt.Foreground =
                            tbCustomCssPrompt.Foreground = Brushes.Black;
                        break;
                    }
                case "presentation_dark":
                    {
                        this.lmeCssFullPath = Globals.PathOfWorkspace + cssFileName + ".css";
                        tbLmeCssPrompt.Text = "LME 内置【暗】系演示主题样式：";
                        tbCustomCssPrompt.Text = "自定义【暗】系演示主题 CSS 片段：";
                        this.customCssFullPath = Globals.PathOfWorkspace + "custom_" + cssFileName + ".css";

                        mainGrid.Background = Brushes.DimGray;
                        gs.Background = Brushes.DarkGray;
                        tbLmeCssPrompt.Foreground =
                            tbCustomCssPrompt.Foreground = Brushes.LightGray;
                        break;
                    }
                default:
                    {
                        LMessageBox.Show($"目前不支持编辑文件名为【{cssFileName}】的 CSS 文件。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
            }

            var workspaceTitle = Globals.MainWindow.GetMetaFileTitleOfDirectory(Globals.PathOfWorkspace);
            if (string.IsNullOrEmpty(workspaceTitle))
            {
                if (Directory.Exists(Globals.PathOfWorkspace))
                {
                    workspaceTitle = new DirectoryInfo(Globals.PathOfWorkspace).Name;
                }
                else workspaceTitle = "<未知标题工作区>";
            }

            spWorkspaceTitle.Inlines.Clear();
            spWorkspaceTitle.Inlines.Add(new Run(workspaceTitle));

            spWorkspaceFullPath.Inlines.Clear();
            spWorkspaceFullPath.Inlines.Add(new Run(Globals.PathOfWorkspace));

            lmeCssEditor.Options.ShowSpaces = false;
            lmeCssEditor.Options.ShowTabs = true;
            lmeCssEditor.Options.ConvertTabsToSpaces = false;
            lmeCssEditor.Options.AutoAlignTabs = false;
            lmeCssEditor.Options.HighlightCurrentLine = true;
            lmeCssEditor.Options.ShowEndOfLine = false;
            lmeCssEditor.ShowLineNumbers = true;

            HighlightingManager.Instance.RegisterHighlighting("CSS Highlighting", new string[] { ".css" }, cssHighlighting);
            customCssEditor.SyntaxHighlighting =
                lmeCssEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".css");

            //如果文件存在，载入之
            if (File.Exists(lmeCssFullPath))
            {
                this.lmeCssEditor.Load(lmeCssFullPath);
                if (this.lmeCssEditor.Text.Trim(new char[] { ' ', '　', '\t', '\r', '\n' }).Length <= 0)
                {
                    this.lmeCssEditor.Text = "@charset \"utf-8\";\r\n";
                }
                this.lmeCssEditor.IsModified = false;
            }

            customCssEditor.Options.ShowSpaces = false;
            customCssEditor.Options.ShowTabs = true;
            customCssEditor.Options.ConvertTabsToSpaces = false;
            customCssEditor.Options.AutoAlignTabs = false;
            customCssEditor.Options.HighlightCurrentLine = true;
            customCssEditor.Options.ShowEndOfLine = false;
            customCssEditor.ShowLineNumbers = true;

            HighlightingManager.Instance.RegisterHighlighting("CSS Highlighting", new string[] { ".css" }, cssHighlighting);
            customCssEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".css");

            // 如果存在上次用户之前编写的 CSS 片段，载入之。
            if (File.Exists(customCssFullPath))
            {
                this.customCssEditor.Load(customCssFullPath);
                this.customCssEditor.IsModified = false;
            }

            findAndReplaceDialog = new FindReplaceDialog(this.customCssEditor);
        }

        private FindReplaceDialog findAndReplaceDialog;

        private string lmeCssFullPath = "";
        /// <summary>
        /// [只读]载入的 LME 默认 CSS 文件在工作区目录中的磁盘路径。可能有明、暗两种。
        /// </summary>
        public string LmeCssFullPath { get { return this.lmeCssFullPath; } }

        private string customCssFullPath = "";
        /// <summary>
        /// [只读]自定义的 CSS 文件在工作区目录中的磁盘路径。可能有明、暗两种。
        /// </summary>
        public string CustomCssFullPath { get { return this.customCssFullPath; } }

        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {
            SaveCustomCssFile();
        }

        private bool SaveCustomCssFile()
        {
            try
            {
                if (customCssEditor.IsModified == false) return true;

                using (StreamWriter sw = new StreamWriter(this.customCssFullPath))
                {
                    sw.Write(this.customCssEditor.Text);
                    customCssEditor.IsModified = false;
                }

                return true;
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }
        }

        private void SwitchComment()
        {
            var e = this.customCssEditor;
            if (e == null) return;

            //这个思路是：
            //  1. 看选择区左右两侧有没有 /* 和 */ 有就取消，没有就在插入点两侧加一对。
            //  2. /* */ 对之间的空白字符会被忽略（全半角空格、Tab符）。
            var trimChars = new char[] { ' ', '　', '\t', };
            var selStart = e.SelectionStart;
            var selEnd = selStart + e.SelectionLength;

            var fstLine = e.Document.GetLineByOffset(selStart);
            var lastLine = e.Document.GetLineByOffset(selEnd);

            var left = e.Document.GetText(fstLine.Offset, selStart - fstLine.Offset);
            var right = e.Document.GetText(selEnd, lastLine.EndOffset - selEnd);

            var indexOfLeftMark = left.LastIndexOf("/*");
            var indexOfRightMark = right.IndexOf("*/");

            if (indexOfLeftMark >= 0 && indexOfRightMark >= 0)
            {
                var leftMarkOffset = fstLine.Offset + indexOfLeftMark;
                var rightMarkOffset = selEnd + indexOfRightMark;
                var innerText = e.Document.GetText(leftMarkOffset + 2, rightMarkOffset - leftMarkOffset - 2);

                if (string.IsNullOrWhiteSpace(innerText)) innerText = "";

                e.BeginChange();
                var leftReplaceOffset = fstLine.Offset + left.LastIndexOf("/*");
                var rightReplaceOffset = selEnd + right.IndexOf("*/");
                e.Document.Replace(leftReplaceOffset, rightReplaceOffset - leftReplaceOffset + 2, innerText);
                e.EndChange();
                return;
            }
            else
            {
                e.BeginChange();
                e.SelectedText = $"/*{e.SelectedText}*/";
                e.EndChange();
                e.Select(selStart + 2, 0);
            }
        }

        private void FormatCssText()
        {
            var text = this.customCssEditor.Text;

            var option = RegexOptions.Multiline;
            Regex reg = new Regex(@"\{[\s\S]*?\}", option);
            // 匹配含换行符在内的所有字符

            var matches = reg.Matches(text);
            if (matches.Count > 0)
            {
                StringBuilder sb = new StringBuilder();
                Match preMatch = null;
                var trimChars = new char[] { ' ', '　', '\t', };

                for (int i = 0; i < matches.Count; i++)
                {
                    Match match = matches[i];
                    if (preMatch == null)
                    {
                        sb.Append(text.Substring(0, match.Index));
                    }
                    else
                    {
                        var preEnd = preMatch.Index + preMatch.Length;
                        sb.Append(FormatOtherCssText(text.Substring(preEnd, match.Index - preEnd).TrimEnd(trimChars)));  //头部空格要保留
                    }

                    sb.Append(FormatCssSegment(match.Value));

                    preMatch = match;
                }

                sb.Append(text.Substring(matches[matches.Count - 1].Index + matches[matches.Count - 1].Length).TrimStart(new char[] { '\r', '\n', }));

                var result = sb.ToString();
                Regex re = GetRegEx(@"(\r\n){3,}");
                result = re.Replace(result, "\r\n\r\n");

                this.customCssEditor.BeginChange();
                this.customCssEditor.Document.Replace(0, this.customCssEditor.Text.Length, result);
                this.customCssEditor.EndChange();
            }
            else return;
        }

        private string FormatOtherCssText(string src)
        {
            var trimChars = new char[] { ' ', '　', '\t', };
            src = src.Replace(">", " > ");
            Regex regex = new Regex(@"[ 　\t]{2,}", RegexOptions.Singleline);
            var result = regex.Replace(src, " ");
            if (result.StartsWith("\r\n")) result = "\r\n" + result.Substring(2).Trim(trimChars) + " ";

            Regex regex2 = new Regex(@"\r\n[ 　\t]{1,}", RegexOptions.Multiline);
            result = regex2.Replace(result, "\r\n");
            return result;
        }

        /// <summary>
        /// 根据“查找/替换”面板中配置的选项，生成正则表达式对象。
        /// </summary>
        /// <param name="textToFind">要查找的文本。</param>
        /// <returns>返回一个正则对象。</returns>
        public Regex GetRegEx(string textToFind)
        {
            RegexOptions options = RegexOptions.None;
            options |= RegexOptions.IgnoreCase;
            options |= RegexOptions.Multiline;

            return new Regex(textToFind, options);
        }

        private string FormatCssSegment(string s)
        {
            if (string.IsNullOrEmpty(s)) return s;
            s = s.Trim(new char[] { '{', '}' });

            var lines = s.Split(new string[] { "\r\n", }, StringSplitOptions.RemoveEmptyEntries);
            StringBuilder sb = new StringBuilder();

            //基本目标：
            //    1. 让片段头部全部对齐，都以4个空格开头，替换掉所有 Tab 字符。
            //    2. 让片段中所有 /**/ 备注对齐，对齐位置为最长的一个行的尾部加两个半角空格；

            Regex regex = new Regex(@"[/][*].*?[*][/]");

            List<CssAttributeInfo> attrInfos = new List<CssAttributeInfo>();
            char[] trimChars = new char[] { '\t', ' ', '　', };
            foreach (var line in lines)
            {
                var txt = line.Trim(trimChars);
                var colonIndex = txt.IndexOf(":");
                var semicolonIndex = txt.IndexOf(";");

                if (colonIndex < 0) continue; //这行无意义。

                if (txt.StartsWith("/*") && txt.EndsWith("*/"))   // 如果整行都被注释了
                {
                    txt = "/*" + txt.Substring(2, txt.Length - 4).Trim(trimChars) + "*/";
                    attrInfos.Add(new CssAttributeInfo() { Comment = txt, OnlyComment = true, });
                    continue;
                }

                var attrName = txt.Substring(0, txt.IndexOf(':')).Trim(trimChars);
                string attrValue;
                string comment;
                if (semicolonIndex >= 0)
                {
                    if (colonIndex > semicolonIndex) continue;  //这行无意义

                    attrValue = txt.Substring(colonIndex + 1, semicolonIndex - colonIndex - 1).Trim(trimChars);
                    if (attrValue.EndsWith(";") == false) attrValue += ";";
                    comment = txt.Substring(semicolonIndex + 1).Trim(trimChars);
                    if (string.IsNullOrWhiteSpace(comment))
                    {
                        comment = "";
                    }
                }
                else
                {
                    attrValue = txt.Substring(colonIndex + 1).Trim(trimChars);
                    var indexOfComment = attrValue.IndexOf("/*");
                    if (indexOfComment >= 0)
                    {
                        attrValue = attrValue.Substring(0, indexOfComment);
                        comment = attrValue.Substring(indexOfComment).Trim(trimChars);
                    }
                    else
                    {
                        comment = "";
                    }
                    if (attrValue.EndsWith(";") == false) attrValue += ";";
                }

                attrInfos.Add(new CssAttributeInfo() { AttrName = attrName, AttrValue = attrValue, Comment = comment, });
            }

            var maxWidth = 0;

            foreach (var info in attrInfos)
            {
                var curWidth = info.AttrTextWidth;
                maxWidth = Math.Max(curWidth, maxWidth);
            }

            maxWidth += 2;  //加2是为了在最长一行 attr 右侧留出两个空格位置再写注释。

            foreach (var info in attrInfos)
            {
                if (info.OnlyComment)
                {
                    sb.Append("    ");
                    sb.Append(info.Comment);
                    sb.Append("\r\n");
                }
                else
                {
                    sb.Append("    ");
                    sb.Append(info.AttrName);
                    sb.Append(": ");
                    sb.Append(info.AttrValue);
                    sb.Append(info.BuildBlanks(info.AttrTextWidth, maxWidth));
                    sb.Append(info.Comment);
                    sb.Append("\r\n");
                }
            }

            return "{\r\n" + sb.ToString() + "}\r\n";
        }

        /// <summary>
        /// Css 文本行信息。
        /// </summary>
        class CssAttributeInfo
        {
            /// <summary>
            /// 整行都被注释掉了。
            /// </summary>
            public bool OnlyComment { get; set; } = false;
            /// <summary>
            /// css 特性名，在冒号左侧。
            /// </summary>
            public string AttrName { get; set; } = "";
            /// <summary>
            /// css 值，在冒号右侧，以分号结尾。
            /// </summary>
            public string AttrValue { get; set; } = "";
            /// <summary>
            /// 名称和值的文本长度，不包括注释长度。这里的“文本长度”是分全、半角的。
            /// </summary>
            public int AttrTextWidth
            {
                get
                {
                    return MarkdownEditorBase.GetTextWidth(AttrName + ": " + AttrValue);
                }
            }
            private string comment = "";
            /// <summary>
            /// 当前 css 行的注释，形如 /*...*/ ，总在行尾。
            /// </summary>
            public string Comment
            {
                get
                {
                    var txt = comment.Trim(new char[] { ' ', '　', '\t', });
                    if (txt.StartsWith("/*")) txt = txt.Substring(2);
                    if (txt.EndsWith("*/")) txt = txt.Substring(0, txt.Length - 2);

                    if (string.IsNullOrWhiteSpace(txt)) return "";

                    return $"/*{txt}*/";
                }
                set { this.comment = value; }
            }
            /// <summary>
            /// 处于当前行注释与当前行值之间的空格，用于对齐注释。
            /// </summary>
            public string BuildBlanks(int attrWidth, int maxWidth)
            {
                return new string(' ', maxWidth - attrWidth);
            }
        }

        private void MetroWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (customCssEditor.IsModified)
            {
                var answer = LMessageBox.Show("编辑区的 CSS 文本已修改，要保存吗？", Globals.AppName,
                    MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, "", null, this);
                switch (answer)
                {
                    case MessageBoxResult.Yes:
                        {
                            SaveCustomCssFile();
                            isClosed = true;
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            isClosed = true;
                            break;
                        }
                    case MessageBoxResult.Cancel:
                        {
                            isClosed = false;
                            e.Cancel = true;
                            break;
                        }
                }
            }

            isClosed = true;
        }

        private bool isClosed = false;

        public bool IsClosed { get { return isClosed; } }

        private void miCut_Click(object sender, RoutedEventArgs e)
        {
            customCssEditor.Cut();
        }

        private void miCopy_Click(object sender, RoutedEventArgs e)
        {
            customCssEditor.Copy();
        }

        private void miPaste_Click(object sender, RoutedEventArgs e)
        {
            customCssEditor.Paste();
        }

        private void miUndo_Click(object sender, RoutedEventArgs e)
        {
            if (customCssEditor.CanUndo) customCssEditor.Undo();
        }

        private void miRedo_Click(object sender, RoutedEventArgs e)
        {
            if (customCssEditor.CanRedo) customCssEditor.Redo();
        }

        private void miSelectAll_Click(object sender, RoutedEventArgs e)
        {
            customCssEditor.SelectAll();
        }

        private void btnFormat_Click(object sender, RoutedEventArgs e)
        {
            FormatCssText();
        }

        private void btnFoldAll_Click(object sender, RoutedEventArgs e)
        {
            foreach (var i in this.lmeCssEditor.FoldingManager.AllFoldings)
            {
                i.IsFolded = true;
            }

            foreach (var i in this.customCssEditor.FoldingManager.AllFoldings)
            {
                i.IsFolded = true;
            }
        }

        private void btnExpandAll_Click(object sender, RoutedEventArgs e)
        {
            foreach (var i in this.lmeCssEditor.FoldingManager.AllFoldings)
            {
                i.IsFolded = false;
            }

            foreach (var i in this.customCssEditor.FoldingManager.AllFoldings)
            {
                i.IsFolded = false;
            }
        }

        private void btnOneToThree_Click(object sender, RoutedEventArgs e)
        {
            if (btnOneToThree.IsChecked == false)
            {
                btnOneToThree.IsChecked = true;
                btnThreeToOne.IsChecked = false;
                cdLeftColumn.Width = new GridLength(1, GridUnitType.Star);
                cdRightColumn.Width = new GridLength(3, GridUnitType.Star);

                bdLme.BorderBrush = Brushes.Transparent;
                bdCustom.BorderBrush = Brushes.SaddleBrown;
                customCssEditor.Focus();
            }
            else ResetCssEditorsLayout();
        }

        private void ResetCssEditorsLayout()
        {
            btnOneToThree.IsChecked = false;
            btnThreeToOne.IsChecked = false;
            cdLeftColumn.Width = new GridLength(1, GridUnitType.Star);
            cdRightColumn.Width = new GridLength(1, GridUnitType.Star);
        }

        private void btnThreeToOne_Click(object sender, RoutedEventArgs e)
        {
            if (btnThreeToOne.IsChecked == false)
            {
                btnOneToThree.IsChecked = false;
                btnThreeToOne.IsChecked = true;
                cdLeftColumn.Width = new GridLength(3, GridUnitType.Star);
                cdRightColumn.Width = new GridLength(1, GridUnitType.Star);

                bdLme.BorderBrush = Brushes.SaddleBrown;
                bdCustom.BorderBrush = Brushes.Transparent;
                lmeCssEditor.Focus();
            }
            else ResetCssEditorsLayout();
        }

        private void btnTopmost_Click(object sender, RoutedEventArgs e)
        {
            this.Topmost = !this.Topmost;
            if (this.Topmost)
            {
                backgroundGridTopMost.Background = Brushes.DarkCyan;
            }
            else
            {
                backgroundGridTopMost.Background = Brushes.Transparent;
            }
        }

        private void MetroWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
            KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            bool isCtrl, isShift, isAlt;

            isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
            isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
            isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

            switch (e.Key)
            {
                case Key.Tab:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            if (bdCustom.BorderBrush == Brushes.SaddleBrown)
                            {
                                ActiveLmeCssEditor();
                            }
                            else
                            {
                                ActiveCustomCssEditor();
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.S:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            SaveCustomCssFile();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F6:
                    {
                        if (isCtrl && isShift && !isAlt)
                        {
                            if (SaveCustomCssFile()) this.Close();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.H:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            // 内置的只能查找，不能替换，所以这里只考虑 customCssEditor
                            findAndReplaceDialog.Owner = this;
                            findAndReplaceDialog.IsReplacePanelVisible = true;
                            findAndReplaceDialog.SetEditor(customCssEditor);
                            findAndReplaceDialog.tabMain.SelectedIndex = 1;
                            findAndReplaceDialog.Show();
                            findAndReplaceDialog.Activate();

                            if (!customCssEditor.TextArea.Selection.IsMultiline)
                            {
                                findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = customCssEditor.TextArea.Selection.GetText();
                                findAndReplaceDialog.txtFind.SelectAll();
                                findAndReplaceDialog.txtFind2.SelectAll();
                                findAndReplaceDialog.txtFind2.Focus();
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F:
                    {
                        if (isCtrl && !isAlt)
                        {
                            if (isShift)
                            {
                                FormatCssText();
                                e.Handled = true;
                            }
                            else
                            {
                                if (bdLme.BorderBrush == Brushes.SaddleBrown)
                                {
                                    FindBuildInCss();
                                    e.Handled = true;
                                }
                                else if (bdCustom.BorderBrush == Brushes.SaddleBrown)
                                {
                                    FindCustomCss();
                                    e.Handled = true;
                                }
                            }
                        }
                        break;
                    }
                case Key.D8:
                    {
                        if (isCtrl && !isShift && !isAlt)  //Ctrl+8
                        {
                            //自动换注释状态
                            SwitchComment();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.T:
                    {
                        if (!isShift && !isAlt && isCtrl)  //Ctrl+T
                        {
                            btnTopmost_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F1:
                    {
                        if (!isShift && !isAlt && !isCtrl)  //F1,左面板折叠或展开
                        {
                            btnOneToThree_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F12:
                    {
                        if (!isShift && !isAlt && !isCtrl)  //F12，右面板折叠或展开
                        {
                            btnThreeToOne_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
            }
        }

        private void FindBuildInCss()
        {
            findAndReplaceDialog.Owner = this;
            findAndReplaceDialog.IsReplacePanelVisible = false; // 内置的 CSS 只能查找，不能替换。
            findAndReplaceDialog.SetEditor(lmeCssEditor);
            findAndReplaceDialog.tabMain.SelectedIndex = 0;
            findAndReplaceDialog.Show();
            findAndReplaceDialog.Activate();

            findAndReplaceDialog.IsReplacePanelVisible = false;

            if (!lmeCssEditor.TextArea.Selection.IsMultiline)
            {
                findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = lmeCssEditor.TextArea.Selection.GetText();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.Focus();
            }
        }

        private void FindCustomCss()
        {

            findAndReplaceDialog.Owner = this;
            findAndReplaceDialog.IsReplacePanelVisible = true;
            findAndReplaceDialog.SetEditor(customCssEditor);
            findAndReplaceDialog.tabMain.SelectedIndex = 0;
            findAndReplaceDialog.Show();
            findAndReplaceDialog.Activate();

            findAndReplaceDialog.IsReplacePanelVisible = true;

            if (!customCssEditor.TextArea.Selection.IsMultiline)
            {
                findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = customCssEditor.TextArea.Selection.GetText();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.Focus();
            }
        }

        private void lmeCssEditor_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            ActiveLmeCssEditor();
        }

        private void ActiveLmeCssEditor()
        {
            lmeCssEditor.Focus();
            bdLme.BorderBrush = Brushes.SaddleBrown;
            bdCustom.BorderBrush = Brushes.Transparent;

            if (findAndReplaceDialog != null)
                findAndReplaceDialog.IsReplacePanelVisible = false;
        }

        private void customCssEditor_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            ActiveCustomCssEditor();
        }

        private void ActiveCustomCssEditor()
        {
            customCssEditor?.Focus();
            bdLme.BorderBrush = Brushes.Transparent;
            bdCustom.BorderBrush = Brushes.SaddleBrown;

            if (findAndReplaceDialog != null)
                findAndReplaceDialog.IsReplacePanelVisible = true;
        }

        private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
        {
            ActiveCustomCssEditor();
        }
    }
}
